package hamaster.jpbc.sys;

import java.io.Serializable;
import java.util.Locale;
import java.util.Set;
import java.util.TimeZone;
import java.util.TreeSet;

import it.unisa.dia.gas.jpbc.Element;
import hamaster.jpbc.pbc.PBCDriver;
import hamaster.jpbc.pbc.PrivateKey;
import hamaster.jpbc.pbc.PublicParameter;
import hamaster.jpbc.pbs.PersonalParameter;
import hamaster.jpbc.util.Hash;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public final class IBCSystem {

	private final static BeanFactory factory;
	private final static IBCConfig config;
	private final static Set<IBCSystem.IBCTimeZone> timeZones;

	static {
		factory = new ClassPathXmlApplicationContext("applicationContext.xml");
		config = (IBCConfig) factory.getBean("ibcconfig");
		timeZones = new TreeSet<IBCSystem.IBCTimeZone>();
		Thread t = new Thread() {
			IBCSystem instace = new IBCSystem();
			@Override
			public void run() {
				String[] ids = TimeZone.getAvailableIDs();
				for (String id : ids) {
					TimeZone tz = TimeZone.getTimeZone(id);
					IBCSystem.IBCTimeZone itz = instace.new IBCTimeZone();
					itz.setDisplayName(tz.getDisplayName());
					itz.setTzID(id);
					timeZones.add(itz);
				}
			}
		};
		t.start();
	}

	private IBCSystem() {
	}

	public final static Object getBean(String name) {
		return factory.getBean(name);
	}

	public final static String hash(String str) {
		return PBCDriver.hex(Hash.sha1(str));
	}

	/**
	 * @return 系统公共参数
	 */
	public final static PublicParameter publicParameter() {
		return config.publicParameter();
	}

	/**
	 * @return 系统主加密密钥
	 */
	public final static Element systemKey() {
		return config.systemKey();
	}

	/**
	 * @return 服务器私钥
	 */
	public final static PrivateKey serverPrivateKey() {
		return config.privateKey();
	}

	/**
	 * @return 系统签名密钥
	 */
	public final static Element systemSignatureKey() {
		return config.systemSignatureKey();
	}

	/**
	 * @return 系统签名参数
	 */
	public final static PersonalParameter signatureParameter() {
		return config.signatureParameter();
	}

	public final static Set<IBCSystem.IBCTimeZone> getTimeZones() {
		return timeZones;
	}

	final public static boolean checkEmail(String email) {
		if (email == null || email.length() < 5)
			return false;
		return email.matches("\\w+(\\.\\w+)*@\\w+(\\.\\w+)+");
	}

	public class IBCTimeZone implements Serializable, Comparable<IBCTimeZone> {
		private static final long serialVersionUID = 971286344004847085L;

		private String displayName;
		private String tzID;
		private transient TimeZone timeZone;

		public IBCTimeZone() {
		}

		public void setTzID(String tzID) {
			this.tzID = tzID;
			timeZone = TimeZone.getTimeZone(tzID);
		}

		public String getTzID() {
			return tzID;
		}

		public void setDisplayName(String displayName) {
			this.displayName = displayName;
		}

		public String getDisplayName() {
			return displayName;
		}

		public String getDisplayName(Locale locale) {
			return timeZone.getDisplayName(locale);
		}

		@Override
		public int hashCode() {
			final int prime = 31;
			int result = 1;
			result = prime * result + getOuterType().hashCode();
			result = prime * result + ((displayName == null) ? 0 : displayName.hashCode());
			return result;
		}

		@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (getClass() != obj.getClass())
				return false;
			IBCTimeZone other = (IBCTimeZone) obj;
			if (!getOuterType().equals(other.getOuterType()))
				return false;
			return compareTo(other) == 0;
		}

		@Override
		public int compareTo(IBCTimeZone timeZone) {
			TimeZone thisTimeZone = TimeZone.getTimeZone(tzID);
			TimeZone thatTimeZone = TimeZone.getTimeZone(timeZone.tzID);
			if (thisTimeZone.hasSameRules(thatTimeZone))
				return 0;
			if (thisTimeZone.getDisplayName().equals(thatTimeZone.getDisplayName()))
				return 0;
			return thisTimeZone.getRawOffset() - thatTimeZone.getRawOffset() > 0 ? 1 : -1;
		}

		private IBCSystem getOuterType() {
			return IBCSystem.this;
		}
	}
}
